/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

/**
 * Node.js proxy for Saiku
 *
 * Use this proxy to develop for the UI without having to install the server.
 * Requests will be proxied to try.meteorite.bi,
 * or a Saiku server installation of your choice.
 *
 * To start the server in HTTP mode,
 * run `node server.js [port] [backend_host] [backend_port]`
 *
 * To start the server in HTTPS mode, you will need generate a self-signed
 * certificate, run the following commands in your shell:
 *
 * $ [sudo] openssl genrsa -out key.pem
 * $ [sudo] openssl req -new -key key.pem -out csr.pem
 * $ [sudo] openssl x509 -req -days 9999 -in csr.pem -signkey key.pem -out cert.pem
 * $ [sudo] rm csr.pem
 *
 * run `node server.js https [port] [backend_host] [backend_port]`
 */

// Newer versions of Node.js use the lower-case argv
const argv = process.ARGV || process.argv;

// Packages
const http = require('http');
const https = require('https');
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const path = require('path');
const app = express();

let port;
let backend_host;
let backend_port;
let backend_path_prefix;
let auth;

if (argv[2] === 'https') {
  port = process.env.C9_PORT || parseInt(argv[3], 10) || 8080;
  backend_host = argv[4] || 'try.meteorite.bi';
  backend_port = parseInt(argv[5], 10) || 80;
  backend_path_prefix = argv[6] || '';
  auth = argv[7] || null;
} else {
  port = process.env.C9_PORT || parseInt(argv[2], 10) || 8080;
  backend_host = argv[3] || 'try.meteorite.bi';
  backend_port = parseInt(argv[4], 10) || 80;
  backend_path_prefix = argv[5] || '';
  auth = argv[6] || null;
}

// Middlewares
app.use(cors());

let standard_prefix =
  process.env.NODE_ENV === 'development'
    ? '/saiku/rest/saiku/'
    : '/api/saiku/rest/saiku';

let init_webapp_url =
  process.env.NODE_ENV === 'development' ? '/saiku/*' : '/api/saiku/*';

// Proxy request
function get_from_proxy(request, response) {
  // if a path prefix is set, remove the existing one
  if (backend_path_prefix !== '') {
    if (request.url.indexOf(standard_prefix) === 0) {
      request.url =
        backend_path_prefix + request.url.substr(standard_prefix.length);
    }
  }

  if (auth) {
    request.headers['authorization'] = `Basic ${new Buffer(auth).toString(
      'base64'
    )}`;
    request.headers['www-authorization'] = `Basic ${new Buffer(auth).toString(
      'base64'
    )}`;

    delete request.headers['cookie'];
  }

  const options = {
    hostname: backend_host,
    port: backend_port,
    path: request.url,
    method: request.method,
    headers: request.headers
  };

  console.log(options.method, options.path);

  const proxy_request = http.request(options);

  request.addListener('data', chunk => {
    proxy_request.write(chunk, 'binary');
  });

  request.addListener('end', () => {
    proxy_request.end();
  });

  proxy_request.addListener('error', error => {
    console.log('ERROR:', error);
  });

  proxy_request.addListener('response', proxy_response => {
    proxy_response.addListener('data', chunk => {
      response.write(chunk, 'binary');
    });

    proxy_response.addListener('end', () => {
      response.end();
    });

    response.writeHead(proxy_response.statusCode, proxy_response.headers);
  });
}

// Handle incoming requests
app.all(init_webapp_url, (request, response) => {
  request.headers.host = backend_host;
  get_from_proxy(request, response);
});

if (argv[2] === 'https') {
  const options = {
    key: fs.readFileSync('key.pem'),
    cert: fs.readFileSync('cert.pem')
  };

  https.createServer(options, app).listen(port, () => {
    console.log('Started!');
  });
} else {
  app.listen(port, '0.0.0.0');
}

console.log('Connected to "', backend_host, ':', backend_port, '"');
console.log('Proxy listening on', port);
